Info's zum nachlesen
Variante des Musters
Man erkennt aus dem UML-Diagramm das es sich hier um eine Variante des Entwurfsmusters handelt. Zeichen dafür sind:
- Das Interfase „Node“ beinhaltet zwar Methoden die grundsätzlich für eine „Node“ Objekt gelten jedoch wird die Methode „accept“ hier nicht deklariert.
- Die abstrakte Klasse „SimpleNode“ wird vom Interface „Node“ abgeleitet, hier werden die Methoden des Interfaces implementiert. Zusätzlich enthält diese Klasse die Methoden „jjtAccept“ und „childrenAccept“.
- Die Elemente des Syntaxbaumes werden anschließen von der „SimpleNode“ Klasse abgeleitet und enthalten die spezifische Implementierung der „jjtAccept“ Methode.
- Zusätzlicher Argument „Objekt data“ bei der Methode „jjtAccept“
Die Elemente
Die Klassen auf der linken Seite des Diagrammes, also all die mit
AST beginnen, stellen die Knoten des Syntaxbaumes dar. Abhängig von dem
eingegebenen Ausdruck wird beim Parsen ein abstrakter Syntaxbaum
aufgebaut welcher aus den hier aufgeführten Elementen besteht. Die
Reihenfolge der Knoten ist abhängig vom eingegebenen Ausdruck. Jedes
dieser Elemente enthält die Implementierung der Methode „jjtAccept“,
sie wird wie folgt implementiert:
public Object jjtAccept(MyParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
Sie ermöglicht den Aufruf der Methode „visit“ für das entsprechende
Element. Zusätzlich können hier noch weitere Daten an die „visit“
Methode übergeben.
Die Klasse „SimpleNode“ und das Interface „Node“
Durch das Einführen der abstrakten Klasse SimpleNode wird die
Implementierung in den Elementen übersichtlich gehalten. Die Klasse
SimpleNode implementiert alle Methoden die das Interface Node
vorschreibt. Somit wird die Implementierung der Methoden in den
Elementen überflüssig. Ein weiterer Vorteil ist dass hier die Methode
childrenAccept exestiert. Hier wird die Traversierung der Baumstruktur
Implementiert. Diese Methode kann optional zu der jjtAccept Mehode
vewendet werden.
public Object childrenAccept(MyParserVisitor visitor, Object data) {
if (children != null) {
for (int i = 0; i < children.length; ++i) {
((SimpleNode) children[i]).jjtAccept(visitor, data);
}
}
return data;
}
So eine Art der Implementierung der Accept Methode zum durchlaufen
von Strukturen kann natürlich verwendet werden ist in dieser Anwendung
jedoch nicht zu gebrauchen. Wir wollen nähmlich die Struktur nicht
einfach nur durchlaufen sondern auch bestimmte Operationen mit den
Objekten durchführen welche die obere Implementierung nicht zulässt.
Es gilt grundsätzlich für den Einsatz des Visitor Patterns:
Als erstes muss überprüft werden ob es eine optimale Variante des Musters für einen specifischen Sachverhalt gibt. Wenn es eine gibt dann sollte dieser auch verwendet werden. Wenn es keine ideale Variante gibt gilt: Zur Navigation auf der Objektstruktur sollte der Code in die Visitor Klasse verlagert werden und nicht in die Elemente.
Das Interface Visitor und dessen Implementierung
Das Visitor Interface enthält für jedes Element der Struktur eine
„visit“ Methode. Die Klasse „MyVisitor“ implementiert das Interface
„Visitor“ und die spezifische Funktionalität der jeweiligen „visit“
Methode. Die Navigation auf der Objektstruktur wird ebenfalls hier
implementiert diese erfolgt rekursiv.
Implementierung:
package ModellChecking; import java.util.*;
public class MyVisitor implements MyParserVisitor {
public Object visit(SimpleNode node, Object data) {
return null; }
public Object visit(ASTStart node, Object data) {
SimpleNode c1 = (SimpleNode)node.jjtGetChild(0);
return (Boolean)c1.jjtAccept(this,data);
}
public Object visit(ASTatomic node, Object data) {
Hashtable belegung = (Hashtable)data;
return new Boolean((Boolean)belegung.get(node.getAtomicExpr()));
}
public Object visit(ASTNOT node, Object data) {
SimpleNode c1 = (SimpleNode)node.jjtGetChild(0);
Boolean r1 = (Boolean)c1.jjtAccept(this,data);
if (true == r1.booleanValue())
return new Boolean(false);
return new Boolean(true);
}
public Object visit(ASTAND node, Object data) {
SimpleNode c1 = (SimpleNode)node.jjtGetChild(0);
SimpleNode c2 = (SimpleNode)node.jjtGetChild(1);
Boolean r1 = (Boolean)c1.jjtAccept(this,data);
Boolean r2 = (Boolean)c2.jjtAccept(this,data);
if (r1.booleanValue() && r2.booleanValue())
return new Boolean(true);
return new Boolean(false);
}
public Object visit(ASTOR node, Object data) {
SimpleNode c1 = (SimpleNode)node.jjtGetChild(0);
SimpleNode c2 = (SimpleNode)node.jjtGetChild(1);
Boolean r1 = (Boolean)c1.jjtAccept(this,data);
Boolean r2 = (Boolean)c2.jjtAccept(this,data);
if (r1.booleanValue() || r2.booleanValue())
return new Boolean(true);
return new Boolean(false);
}
public Object visit(ASTIMPLIES node, Object data) {
SimpleNode c1 = (SimpleNode)node.jjtGetChild(0);
SimpleNode c2 = (SimpleNode)node.jjtGetChild(1);
Boolean r1 = (Boolean)c1.jjtAccept(this,data);
Boolean r2 = (Boolean)c2.jjtAccept(this,data);
if (!(r2.booleanValue()))
{
if (r1.booleanValue())
return new Boolean(false);
}
return new Boolean(true);
}
public Object visit(ASTPHI node, Object data) {
return null;
}
public Object visit(ASTconstant node, Object data) {
return new Boolean(false);
}
}